module dogre.math;

import dogre.internals.utils;
import std.string;
import dogre.internals.utils;

extern(C)
{
    void* getRadian(float r);
    void* op_Radian_Unary(void* ptr, char op);
    void* opRadian_Radian_Binary_Radian(void* ptr, const(char)* op, void* rad2);
    bool opBool_Radian_Binary_Radian(void* ptr, const(char)* op, void* rad2);
    void* opRadian_Radian_Binary_Float(void* ptr, const char *op, float fl);
    float call_Radian_valueRadians(void* ptr);
    
    void* getDegree(float r);
    void* op_Degree_Unary(void* ptr, char op);
    void* opDegree_Degree_Binary_Degree(void* ptr, const(char)* op, void* rad2);
    bool opBool_Degree_Binary_Degree(void* ptr, const(char)* op, void* rad2);
    void* opDegree_Degree_Binary_Float(void* ptr, const char *op, float fl);
    float call_Degree_valueRadians(void* ptr);
    float call_Degree_valueDegrees(void* ptr);
}



class Radian : AngleMeasurement  //The C++ implements no way to get the ptr of mRad :(
{
    mixin(Wrapper);
    
    this(float r)
    {
        p = getRadian(r);
    }
    
    void* asRadians()
    {
        return p;
    }
    
    float valueRadians()
    {
        return call_Radian_valueRadians(p);
    }
    
    Radian opBinary(string op)(Radian other) if(op == "+" || op == "-")
    {
        final switch(op)
        {
        case "+":
            return new Radian(opRadian_Radian_Binary_Radian(p, toStringz("+"), other.p));
        case "-":
            return new Radian(opRadian_Radian_Binary_Radian(p, toStringz("-"), other.p));
        case "*":
            return new Radian(opRadian_Radian_Binary_Radian(p, toStringz("*"), other.p));
        }
    }
    
    bool opEquals(Object obj)
    {
        Radian other = cast(Radian)obj;
        if(other is null)
            return false;
        return opBool_Radian_Binary_Radian(p, toStringz("=="), other.p);
    }
    
    int opCmp(Radian rad, Radian other)
    {
        if(opBool_Radian_Binary_Radian(p, toStringz("<="), other.p))
        {
            if(opBool_Radian_Binary_Radian(p, toStringz("<"), other.p))
                {return -1;}
        }
        else  //Other is less than or equal to.
        {
            if(opBool_Radian_Binary_Radian(p, toStringz(">"), other.p))
                {return 1;}
        }
        return 0;  //Other is equal to.
    }
    
    //Radian opAssign(Radian other)
    //{
    //    return new Radian(opRadian_Radian_Binary_Radian(p, toStringz("="), other.p));
    //}           ILLEGAL
    
    Radian opAssign(float other)
    {
        return new Radian(opRadian_Radian_Binary_Float(p, toStringz("="), other));
    }
    
    Radian opOpAssign(string op)(Radian other)
    {
        final switch(op)
        {
        case "+=":
            return new Radian(opRadian_Radian_Binary_Radian(p, toStringz("+="), other.p));
        case "-=":
            return new Radian(opRadian_Radian_Binary_Radian(p, toStringz("-="), other.p));
        }
    }
    
    Radian opOpAssign(string op)(float other)
    {
        final switch(op)
        {
        case "*=":
            return new Radian(opRadian_Radian_Binary_Float(p, toStringz("*="), other));
        case "/=":
            return new Radian(opRadian_Radian_Binary_Float(p, toStringz("/="), other));
        }
    }
}



class Degree : AngleMeasurement
{
    mixin(Wrapper);
    
    this(float r)
    {
        p = getDegree(r);
    }
    
    void* asRadians()
    {
        return getRadian(call_Degree_valueRadians(p));
    }
    
    float valueDegrees()
    {
        return call_Degree_valueDegrees(p);
    }
    
    float valueRadians()
    {
        return call_Degree_valueRadians(p);
    }
    
    Degree opBinary(string op)(Degree other) if(op == "+" || op == "-")
    {
        final switch(op)
        {
        case "+":
            return new Degree(opDegree_Degree_Binary_Degree(p, toStringz("+"), other.p));
        case "-":
            return new Degree(opDegree_Degree_Binary_Degree(p, toStringz("-"), other.p));
        case "*":
            return new Degree(opDegree_Degree_Binary_Degree(p, toStringz("*"), other.p));
        }
    }
    
    bool opEquals(Object obj)
    {
        Degree other = cast(Degree)obj;
        if(other is null)
            return false;
        return opBool_Degree_Binary_Degree(p, toStringz("=="), other.p);
    }
    
    int opCmp(Degree rad, Degree other)
    {
        if(opBool_Degree_Binary_Degree(p, toStringz("<="), other.p))
        {
            if(opBool_Degree_Binary_Degree(p, toStringz("<"), other.p))
                {return -1;}
        }
        else  //Other is less than or equal to.
        {
            if(opBool_Degree_Binary_Degree(p, toStringz(">"), other.p))
                {return 1;}
        }
        return 0;  //Other is equal to.
    }

    Degree opAssign(float other)
    {
        return new Degree(opDegree_Degree_Binary_Float(p, toStringz("="), other));
    }
    
    Degree opOpAssign(string op)(Degree other)
    {
        final switch(op)
        {
        case "+=":
            return new Degree(opDegree_Degree_Binary_Degree(p, toStringz("+="), other.p));
        case "-=":
            return new Degree(opDegree_Degree_Binary_Degree(p, toStringz("-="), other.p));
        }
    }
    
    Degree opOpAssign(string op)(float other)
    {
        final switch(op)
        {
        case "*=":
            return new Degree(opDegree_Degree_Binary_Float(p, toStringz("*="), other));
        case "/=":
            return new Degree(opDegree_Degree_Binary_Float(p, toStringz("/="), other));
        }
    }
}